#include <stdint.h>  
#include <unistd.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <getopt.h>  
#include <fcntl.h>  
#include <sys/ioctl.h>  
#include <linux/types.h>  
#include <linux/spi/spidev.h>  
  
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))  
#define RX_TX_MAX 1024
 
static void pabort(const char *s)  
{  
    perror(s);  
    abort();  
}  
  
static const char *device = "/dev/spidev0.0";  
static uint8_t mode;  
static uint8_t bits = 8;  
static uint32_t speed = 1000000;  
static uint16_t delay; 
static char* file_name;
static uint32_t number_byte=0;
static uint8_t read_write_flg=0;	//0-read 1-write
 
int spi_fd;
 
  
static void read_date(int addr,char *buff,int size)  
{  
    int ret,i;  
    uint8_t rx[RX_TX_MAX];
    uint8_t tx[RX_TX_MAX] = {  
        0x03, 0x00, 0x00, 0x00,0x00,
     
    };  
    
    tx[1]=(addr&0xFF0000)>>16;
    tx[2]=(addr&0xFF00)>>8;
    tx[3]=addr&0xFF;
    
    struct spi_ioc_transfer tr = {  
        .tx_buf = (unsigned long)tx,  
        .rx_buf = (unsigned long)rx,  
        .len = size+4,  
        .delay_usecs = delay,  
        .speed_hz = speed,  
        .bits_per_word = bits,  
    };  
  
    ret = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr);  
    if (ret < 1)  
        pabort("can't send spi message");  
        
    for(i=0;i<size;i++)
    {
    	buff[i]=rx[i+4];
    } 
}  
 
static void read_id()  
{  
    int ret;  
    uint8_t rx[4];
    uint8_t tx[4] = {  
        0x9f, 0xef, 0x00, 0x00,   
    };  
    
    struct spi_ioc_transfer tr = {  
        .tx_buf = (unsigned long)tx,  
        .rx_buf = (unsigned long)rx,  
        .len = 4,  
        .delay_usecs = delay,  
        .speed_hz = speed,  
        .bits_per_word = bits,  
    };  
  
    ret = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr);  
    if (ret < 1)  
        pabort("can't send spi message");  
        
    printf("%.2X %.2X \n", rx[2],rx[3]);  
     
}  
 
 
static char read_reg1()  
{  
    int ret; 
    uint8_t rx[2]; 
    uint8_t tx[2] = {  
        0x05, 0x00,
     
    };  
    
    struct spi_ioc_transfer tr = {  
        .tx_buf = (unsigned long)tx,  
        .rx_buf = (unsigned long)rx,  
        .len = 2,  
        .delay_usecs = delay,  
        .speed_hz = speed,  
        .bits_per_word = bits,  
    };  
  
    ret = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr);  
    if (ret < 1)  
        pabort("can't send spi message");  
        
    return rx[1];  
} 
 
static char read_reg2()  
{  
    int ret;
    uint8_t rx[2];  
    uint8_t tx[2] = {  
        0x35, 0x00,
     
    };  
    
    struct spi_ioc_transfer tr = {  
        .tx_buf = (unsigned long)tx,  
        .rx_buf = (unsigned long)rx,  
        .len = 2,  
        .delay_usecs = delay,  
        .speed_hz = speed,  
        .bits_per_word = bits,  
    };  
  
    ret = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr);  
    if (ret < 1)  
        pabort("can't send spi message");  
        
    return rx[1];  
}   
 
static void write_en()  
{  
    int ret;
    uint8_t rx[1];   
    uint8_t tx[1] = {  
        0x06,
    };  
    
    struct spi_ioc_transfer tr = {  
        .tx_buf = (unsigned long)tx,  
        .rx_buf = (unsigned long)rx,  
        .len = 1,  
        .delay_usecs = delay,  
        .speed_hz = speed,  
        .bits_per_word = bits,  
    };  
  
    ret = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr);  
    if (ret < 1)  
        pabort("can't send spi message");     
}  
 
static void write_dis()  
{  
    int ret;  
    uint8_t rx[1];
    uint8_t tx[1] = {  
        0x04,
    };  
    
    struct spi_ioc_transfer tr = {  
        .tx_buf = (unsigned long)tx,  
        .rx_buf = (unsigned long)rx,  
        .len = 1,  
        .delay_usecs = delay,  
        .speed_hz = speed,  
        .bits_per_word = bits,  
    }; 
    
     ret = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr);  
    if (ret < 1)  
        pabort("can't send spi message");      
}  
 
static void BlockErase64k (int addr)  
{  
    int ret; 
    uint8_t rx[4]; 
    uint8_t tx[4] = {  
        0xd8,0x00,0x00,0x00,
    };  
    
    tx[1]=(addr&0xFF0000)>>16;
    
    write_en(spi_fd);
   	while(read_reg1(spi_fd)&0x01==0x01);
   	
    struct spi_ioc_transfer tr = {  
        .tx_buf = (unsigned long)tx,  
        .rx_buf = (unsigned long)rx,  
        .len = 4,  
        .delay_usecs = delay,  
        .speed_hz = speed,  
        .bits_per_word = bits,  
    }; 
    
     ret = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr);  
    if (ret < 1)  
        pabort("can't send spi message"); 
        
     while(read_reg1(spi_fd)&0x01==0x01);     
} 
 
 
static void write_data_page(int addr,char *tx_buff,int size)  
{  
    int ret,i;  
    uint8_t rx[256+4]; 
    char tx[256+4];
    tx[0] =0x02; 
    tx[1]=(addr&0xFF0000)>>16;
    tx[2]=(addr&0xFF00)>>8;
    tx[3]=addr&0xFF;
    
    write_en(spi_fd);
    for(i=0;i<256;i++)
    {
    	tx[4+i]=tx_buff[i];
    }
    
    struct spi_ioc_transfer tr = {  
        .tx_buf = (unsigned long)tx,  
        .rx_buf = (unsigned long)rx,  
        .len = size+4,  
        .delay_usecs = delay,  
        .speed_hz = speed,  
        .bits_per_word = bits,  
    };  
  
    ret = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr);  
    if (ret < 1)  
        pabort("can't send spi message");  
        
    while(read_reg1(spi_fd)&0x01==0x01);
}   
  
 
  
  
static void print_usage(const char *prog)  
{  
    printf("Usage: %s [-DsbdlHOLC3]\n", prog);  
    puts("  -D --device   device to use (default /dev/spidev1.1)\n"  
         "  -s --speed    max speed (Hz)\n"  
         "  -d --delay    delay (usec)\n"  
         "  -b --bpw      bits per word \n"  
         "  -r --read     read img \n" 
         "  -w --writ     write img \n" 
         "  -n --number   Number of bytes read or written \n" 
         "  -l --loop     loopback\n"  
         "  -H --cpha     clock phase\n"  
         "  -O --cpol     clock polarity\n"  
         "  -L --lsb      least significant bit first\n"  
         "  -C --cs-high  chip select active high\n"  
         "  -3 --3wire    SI/SO signals shared\n");  
    exit(1);  
}  
  
static void parse_opts(int argc, char *argv[])  
{  
    while (1) {  
        static const struct option lopts[] = {  
            { "device",  1, 0, 'D' },  
            { "speed",   1, 0, 's' },  
            { "delay",   1, 0, 'd' },  
            { "bpw",     1, 0, 'b' },
            { "read",    1, 0, 'r' }, 
            { "write",   1, 0, 'w' },  
            { "number",  1, 0, 'n' }, 
            { "loop",    0, 0, 'l' },  
            { "cpha",    0, 0, 'H' },  
            { "cpol",    0, 0, 'O' },  
            { "lsb",     0, 0, 'L' },  
            { "cs-high", 0, 0, 'C' },  
            { "3wire",   0, 0, '3' },  
            { "no-cs",   0, 0, 'N' },  
            { "ready",   0, 0, 'R' }, 
            
            { NULL, 0, 0, 0 },  
        };  
        int c;  
  
        c = getopt_long(argc, argv, "D:s:d:b:r:w:n:lHOLC3NR", lopts, NULL);  
  
        if (c == -1)  
            break;  
  
        switch (c) {  
        case 'D':  
            device = optarg;  
            break;  
        case 's':  
            speed = atoi(optarg);  
            break;  
        case 'd':  
            delay = atoi(optarg);  
            break;  
        case 'b':  
            bits = atoi(optarg);  
            break;  
        case 'r':
        		read_write_flg =1;  
            file_name =optarg;  
            printf("file %s\n",file_name);
            break; 
        case 'w':
        		read_write_flg =2;   
            file_name = optarg;   
            break; 
        case 'n':  
            number_byte = atoi(optarg);  
            break; 
        case 'l':  
            mode |= SPI_LOOP;  
            break;  
        case 'H':  
            mode |= SPI_CPHA;  
            break;  
        case 'O':  
            mode |= SPI_CPOL;  
            break;  
        case 'L':  
            mode |= SPI_LSB_FIRST;  
            break;  
        case 'C':  
            mode |= SPI_CS_HIGH;  
            break;  
        case '3':  
            mode |= SPI_3WIRE;  
            break;  
        case 'N':  
            mode |= SPI_NO_CS;  
            break;  
        case 'R':  
            mode |= SPI_READY;  
            break;  
        default:  
            print_usage(argv[0]);  
            break;  
        }  
    }  
} 
 
 
void write_img()
{
	FILE *file = fopen(file_name,"rb");
	if(file==0)
	{
		printf("open error for \"%s\"", file_name); 
		return;
	}
    
   	//擦除块
   	int i;
   	printf("Eraseing...\n"); 
        for (i = 0; i < (number_byte/64/1024); i++) { 
    		BlockErase64k(i*64*1024);
    		printf("Block Erase for %d block %dkB-%dkB\n\33[1A", i,i*64,(i+1)*64); 
        }
        printf("\nErase finish!\n"); 
    
        printf("writing...\n");
        //给芯片编程
  	char buff_data[256];
   	for (i = 0; i < (number_byte/256); i++) {  
   		 
        fread(&buff_data,1,256,file);      
        write_data_page(i*256,buff_data,256); 
        if(i%256==0)   
		    	printf("%dkB\n\33[1A", i*256/1024); 
    } 
		 
    fclose(file);
    write_dis();
    
    printf("\nwrite finish!\n");
}
 
void read_img()
{
    	FILE *file = fopen(file_name,"wb");
	if(file==0)
	{
	    printf("open error for \"%s\"", file_name); 
	    return;
	}
	printf("reading...\n"); 
        char buff_data[256];
  	int i;
   	for (i = 0; i < (number_byte)/256; i++) {  
        read_date(i*256,buff_data,256);
        fwrite(&buff_data,1,256,file);
		   	if(i%256==0)   
		    	printf("%dkB\n\33[1A", i*256/1024); 	
	    	
    } 
    fclose(file);
    write_dis();
    printf("\nread finish!\n");
}  
  
int main(int argc, char *argv[])  
{  
    int ret = 0;  
     
    int i;
    char aa[256]; 
  
    parse_opts(argc, argv);  
  
    spi_fd = open(device, O_RDWR);  
    if (spi_fd < 0)  
        pabort("can't open device");  
  
    /* 
     * spi mode 
     */  
    ret = ioctl(spi_fd, SPI_IOC_WR_MODE, &mode);  
    if (ret == -1)  
        pabort("can't set spi mode");  
  
    ret = ioctl(spi_fd, SPI_IOC_RD_MODE, &mode);  
    if (ret == -1)  
        pabort("can't get spi mode");  
  
    /* 
     * bits per word 
     */  
    ret = ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &bits);  
    if (ret == -1)  
        pabort("can't set bits per word");  
  
    ret = ioctl(spi_fd, SPI_IOC_RD_BITS_PER_WORD, &bits);  
    if (ret == -1)  
        pabort("can't get bits per word");  
  
    /* 
     * max speed hz 
     */  
    ret = ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);  
    if (ret == -1)  
        pabort("can't set max speed hz");  
  
    ret = ioctl(spi_fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);  
    if (ret == -1)  
        pabort("can't get max speed hz");  
  
    printf("spi mode: %d\n", mode);  
    printf("bits per word: %d\n", bits);  
    printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);  
       
    read_id();
    
    //write
    if(read_write_flg==1)
    {
    		read_img();
    }
    else if(read_write_flg==2)
    {
    		write_img();		
    }
    return ret;  
}
